#pragma once
// 需要设置套接字，监听，accpet等待

#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "log.hpp"
#include "errno.hpp"

#define DEFAULEBACKLOGS 50
#define DEFAULTLISTENSOCKET -1
#define DEFAULTPORT 8080

class Sock
{
public:
    Sock()
        : _listensock(DEFAULTLISTENSOCKET)
    {
        _clinetIP = "";
        _clinetPort = DEFAULTPORT;
    }

    ~Sock()
    {
        if (_listensock != DEFAULTLISTENSOCKET)
            close(_listensock);
    }

    int getListenSocket()
    {
        return _listensock;
    }

public:
    void setSocket()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);

        if (_listensock < 0)
        {
            logMessage(FATAL, "create listensock erron");
            exit(SOCKET_ERRON);
        }

        logMessage(SUCESS,"create listensock sucess");

        // 允许该机器不同的ip创建同一端口
        int opt = 1;
        socklen_t opts = sizeof(opt);
        int n = setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR, &opt, opts);
        if (n == -1)
        {
            logMessage(FATAL, "%d setsockopt fail", _listensock);
            exit(SETSOCKOPT_ERRON);
        }

        n = setsockopt(_listensock, SOL_SOCKET, SO_REUSEPORT, &opt, opts);
        if (n == -1)
        {
            logMessage(FATAL, "%d setsockopt fail", _listensock);
            exit(SETSOCKOPT_ERRON);
        }

         logMessage(SUCESS,"%d setsockopt sucess", _listensock);
    }

    void Bind(int port)
    {
        struct sockaddr_in address;
        address.sin_port = htons(port);
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = INADDR_ANY;
        socklen_t solent = sizeof(address);

        int n = bind(_listensock, (const sockaddr *)&address, solent);
        if (n == -1)
        {
            logMessage(FATAL, "%d Bind Erron", _listensock);
            exit(BIND_ERRON);
        }

            logMessage(SUCESS, "%d Bind sucess", _listensock);

    }

    // 全底层队列的大小暂时设为50
    void Listen(int backlog = DEFAULEBACKLOGS)
    {
        int n = listen(_listensock, backlog);

        if (n < 0)
        {
            logMessage(FATAL, "set %d listensock erron", _listensock);
            exit(LISTEN_ERRON);
        }

        logMessage(SUCESS, "set %d listensock sucess", _listensock);

    }

    // Accpet链接套接字,只要建立建立成功就可以开始通信了
    //要记得这个sock的未来的关闭
    int Accpet()
    {
        // 返回最终sock
        struct sockaddr_in address;
        memset(&address, 0, sizeof(address));
        socklen_t addsize = sizeof(address);
        int sock = accept(_listensock, (struct sockaddr *)&address, &addsize);

        // 这个结构体由操作系统填充

        // 如果该套接字大于0，则表示该套接字已经完全准备好了，可以开始通信，sock < 0 链接失败也没关系
        if (sock > 0)
        {
            _clinetIP = inet_ntoa(address.sin_addr);
            _clinetPort = ntohs(address.sin_port);
            logMessage(SUCESS, "set %d Accept sucess", _listensock);
            return sock;
        }
        else
        {
            logMessage(WARNING, "The listening socket %d did not receive the link", _listensock);
            return -1;
        }
    }

    // 今天，我们关心客户的ip和port，可以稍微写一下哈哈
public:
    std::string _clinetIP;
    uint16_t _clinetPort;

private:
    int _listensock;
};
